package paim.gh.view.paineis.arvore;

import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTree;
import javax.swing.event.EventListenerList;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import paim.wingchun.app.WC_Properties;
import paim.wingchun.controller.WC_Event;
import paim.wingchun.model.pojos.Pojo;
import paim.wingchun.view.FireListener;
import paim.wingchun.view.Fireable;
import paim.wingchun.view.GUIUtils;

import paim.gh.model.pojos.Aula;
import paim.gh.model.pojos.Balde;
import paim.gh.model.pojos.Grade;
import paim.gh.model.pojos.Professor;
import paim.gh.model.pojos.Turma;

public final class Arvore extends JTree implements MouseListener, Fireable<ArvoreListener> {

    private static final long serialVersionUID = 1L;

    public enum Eventos {
        NOTHING, SELECAO, ADD, ADDALL, REMOVE, REMOVEALL, SHOW, SHOWALL, HIDE, HIDEALL, ADDTURMA, REMOVETURMA;
    }


    public Arvore() {
        this(new ArvoreModel());
    }

    public Arvore(TreeModel newModel) {
        super(newModel);
        addMouseListener(this);
        addTreeSelectionListener(new FireListener(this, Eventos.SELECAO));
    }

    public Arvore(TreeNode root, boolean asksAllowsChildren) {
        super(root, asksAllowsChildren);
    }

    public Arvore(TreeNode root) {
        super(root);
    }

    @Override
    public EventListenerList getListenersList() {
        /* agora usando a lista de JComponent */
        return this.listenerList;
    }

    @Override
    public void fire(WC_Event e) {
//        ArvoreListener[] listeners = getListenersList().getListeners(ArvoreListener.class);
        for ( ArvoreListener listener : fireListeners() ) {
            switch ( (Eventos) e.evento() ) {
                case ADD:
                    listener.add();
                break;
                case ADDALL:
                    listener.addAll();
                break;
                case SHOW:
                    listener.show();
                break;
                case SHOWALL:
                    listener.showAll();
                break;
                case HIDE:
                    listener.hide();
                break;
                case HIDEALL:
                    listener.hideAll();
                break;
                case REMOVE:
                    listener.remove();
                break;
                case REMOVEALL:
                    listener.removeAll();
                break;

                case ADDTURMA:
                    listener.addTurma();
                break;
                case REMOVETURMA:
                    listener.removeTurma();
                break;

                case SELECAO:
                    listener.selecao();
                break;

                default:
                break;
            }
        }
    }

    private void menuShow(MouseEvent e) {
        /* se nao for botao direita cai fora */
        if ( e.getButton() != MouseEvent.BUTTON3 )
            return;

        /* pega a arvore */
        JTree jTree = (JTree) e.getComponent();
        /* o caminho para o nodo no click */
        TreePath treePath = jTree.getPathForLocation(e.getX(), e.getY());
        /* seleciona o nodo conforme o caminho */
        jTree.setSelectionPath(treePath);

        /* se nao for ArvoreNode cai fora */
        if ( !ArvoreNode.class.isAssignableFrom(jTree.getLastSelectedPathComponent().getClass()) )
            return;

        ArvoreNode nodoSelecionado = (ArvoreNode) jTree.getLastSelectedPathComponent();

        if ( e.isPopupTrigger() && nodoSelecionado != null ) {
            getPopupMenu(nodoSelecionado).show(jTree, e.getX(), e.getY());
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        // System.out.println("mouseClicked");
    }

    @Override
    public void mousePressed(MouseEvent e) {
        menuShow(e);
        // System.out.println("mousePressed");
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        menuShow(e);
        // System.out.println("mouseReleased");
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        /* quando entra no painel da arvore */
        // System.out.println("mouseEntered");
    }

    @Override
    public void mouseExited(MouseEvent e) {
        /* quando sai do painel da arvore */
        // System.out.println("mouseExited");
    }

    public void actionPerformed(ActionEvent e) {
        DefaultMutableTreeNode dmtn, node;

        TreePath path = this.getSelectionPath();
        dmtn = (DefaultMutableTreeNode) path.getLastPathComponent();
        if ( e.getActionCommand().equals("insert") ) {
            node = new DefaultMutableTreeNode("children");
            dmtn.add(node);
            // thanks to Yong Zhang for the tip for refreshing the tree structure.
            ((DefaultTreeModel) this.getModel()).nodeStructureChanged(dmtn);
        }
        if ( e.getActionCommand().equals("remove") ) {
            node = (DefaultMutableTreeNode) dmtn.getParent();
            // Bug fix by essam
            int nodeIndex = node.getIndex(dmtn); // declare an integer to hold the selected nodes index
            dmtn.removeAllChildren(); // remove any children of selected node
            node.remove(nodeIndex); // remove the selected node, retain its siblings
            ((DefaultTreeModel) this.getModel()).nodeStructureChanged(dmtn);
        }

    }

    public JPopupMenu getPopupMenu(ArvoreNode node) {
        JPopupMenu popupMenu = new JPopupMenu();
        /* FIXME esses show e hide nao deveria ser coisas do filtro */
        Class<? extends Pojo> classe = node.getClasse();

        Map<Eventos, Boolean> mapEvento = new LinkedHashMap<>();
        /** nodo Grade */
        if ( Grade.class.isAssignableFrom(classe) ) {
            mapEvento.put(Eventos.SHOWALL, true);
            mapEvento.put(Eventos.HIDEALL, true);
            mapEvento.put(Eventos.NOTHING, null);
            /* mapEvento.put(Evento.REMOVE, true); */
        }
        /** nodo Professor */
        else if ( Professor.class.isAssignableFrom(classe) ) {
            /* se for um professor */
            if ( node.isNodeObjeto() ) {
                mapEvento.put(Eventos.SHOW, true);
                mapEvento.put(Eventos.HIDE, true);
                mapEvento.put(Eventos.NOTHING, null);
                /* mapEvento.put(Evento.REMOVE, true); */
            }
            /* lista de professores */
            else {
                mapEvento.put(Eventos.SHOWALL, !node.isLeaf());
                mapEvento.put(Eventos.HIDEALL, !node.isLeaf());
            }
        }
        /** nodo Aula */
        else if ( Aula.class.isAssignableFrom(classe) ) {
            /* se for uma aula */
            if ( node.isNodeObjeto() ) {
                mapEvento.put(Eventos.SHOW, true);
                mapEvento.put(Eventos.HIDE, true);
                mapEvento.put(Eventos.NOTHING, null);
                mapEvento.put(Eventos.ADDTURMA, true);
                mapEvento.put(Eventos.NOTHING, null);
            }
            /* lista de aulas */
            else {
                mapEvento.put(Eventos.SHOWALL, !node.isLeaf());
                mapEvento.put(Eventos.HIDEALL, !node.isLeaf());
            }
        }
        /* nodo Turma de aula */
        else if ( Turma.class.isAssignableFrom(classe) ) {
            mapEvento.put(Eventos.SHOW, true);
            mapEvento.put(Eventos.HIDE, true);
            mapEvento.put(Eventos.NOTHING, null);
            mapEvento.put(Eventos.REMOVETURMA, true);
            mapEvento.put(Eventos.NOTHING, null);
        }
        /** nodo Balde */
        else if ( Balde.class.isAssignableFrom(classe) ) {
            mapEvento.put(Eventos.ADDALL, true);
            mapEvento.put(Eventos.REMOVEALL, true);
        }
        /** se for nodo descendente do balde entao */
        else if ( node.isNodeAncestor(((ArvoreModel) getModel()).getNodeBalde()) ) {
            if ( node.isNodeObjeto() ) {
                mapEvento.put(Eventos.SHOW, true);
                mapEvento.put(Eventos.HIDE, true);
                mapEvento.put(Eventos.NOTHING, null);
                mapEvento.put(Eventos.REMOVE, true);
            }
            else {
                // // TODO deve ser sensivel a numero de filhos.....
                // node.getChildCount();
                mapEvento.put(Eventos.ADD, true);
                mapEvento.put(Eventos.ADDALL, node.isLeaf());
                mapEvento.put(Eventos.NOTHING, null);
                mapEvento.put(Eventos.REMOVEALL, !node.isLeaf());
            }
        }
        else if ( node.isNodeObjeto() && node.isNodeAncestor(((ArvoreModel) getModel()).getNodeBalde()) ) {
            mapEvento.put(Eventos.SHOW, true);
            mapEvento.put(Eventos.HIDE, true);
            mapEvento.put(Eventos.NOTHING, null);
            mapEvento.put(Eventos.REMOVE, true);
        }
        else {
            mapEvento = Collections.emptyMap();
        }

        /* monta o menu */
        for ( Eventos evento : mapEvento.keySet() ) {
            if ( Eventos.NOTHING == evento )
                popupMenu.add(new JSeparator());
            else {
                String texto = WC_Properties.getProperty("tooltip.tree." + evento.name().toLowerCase());
                ImageIcon imageIcon = GUIUtils.getImageIcon("icon.tree." + evento.name().toLowerCase());
                JMenuItem menuItem = new JMenuItem(texto, imageIcon);
                menuItem.setToolTipText(texto);
                menuItem.setEnabled(mapEvento.get(evento));
                menuItem.addActionListener(new FireListener(this, evento));
                popupMenu.add(menuItem);
            }
        }

        popupMenu.setOpaque(true);
        popupMenu.setLightWeightPopupEnabled(true);

        return popupMenu;
    }

    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
    }

    // FIXME ou deveria estar mesmo em jtreegh e consultar via modelo????? aCHO QUE EM MODELO
    public boolean canAddAll() {
        return true;
    }

    public boolean canAdd() {
        return true;
    }

    public boolean canRemoveAll() {
        return true;
    }

    public boolean canRemove() {
        return true;
    }

}
